package pl.shenlon.ga;

import java.util.HashMap;
import java.util.Map;
import java.util.Random;

import pl.shenlon.poker.game.GameState;

public class StatefulChromosome implements Genom {

	private static GameState[] gameStates;

	public static Random rnd;
	static {
		rnd = new Random();
		gameStates = new GameState[GameState.POSSIBLE_GAME_STATES.size()];
		gameStates = GameState.POSSIBLE_GAME_STATES.toArray(gameStates);
	}

	private final Map<GameState, BehaviourPattern> genom;

	public StatefulChromosome() {
		genom = new HashMap<GameState, BehaviourPattern>();
		for (GameState key : GameState.POSSIBLE_GAME_STATES) {
			genom.put(key, BehaviourPattern.createRandomizedBehaviourPattern());
		}
	}

	public StatefulChromosome(Map<GameState, BehaviourPattern> map) {
		genom = new HashMap<GameState, BehaviourPattern>();
		for (GameState key : map.keySet()) {
			this.genom.put(key, new BehaviourPattern(map.get(key)));
		}
	}

	public StatefulChromosome(StatefulChromosome genom) {
		this.genom = new HashMap<GameState, BehaviourPattern>();
		for (GameState key : GameState.POSSIBLE_GAME_STATES) {
			this.genom.put(key, new BehaviourPattern(genom.genom.get(key)));
		}
	}

	@Override
	public Genom copy() {
		return new StatefulChromosome(this);
	}

	@Override
	public boolean equals(Object other) {
		if (other != null && other instanceof StatefulChromosome) {
			if (other == this) {
				return true;
			}
			StatefulChromosome that = (StatefulChromosome) other;
			return this.genom.equals(that.genom);
		}
		return false;
	}

	@Override
	public BehaviourPattern getBehaviourPattern(GameState gameState) {
		return genom.get(gameState);
	}

	@Override
	public boolean mutate(double mutationRate) {
		if (rnd.nextDouble() <= mutationRate) {
			int patternsToMutate = rnd.nextInt(genom.size());
			

			for (int i = 0; i < patternsToMutate; i++) {
				BehaviourPattern behaviourPattern = genom.get(gameStates[rnd
						.nextInt(genom.size())]);

				int indexToSubstract = rnd
						.nextInt(behaviourPattern.getLength()), indexToAdd = rnd
						.nextInt(behaviourPattern.getLength());
				while (indexToAdd == indexToSubstract) {
					indexToAdd = rnd.nextInt(behaviourPattern.getLength());
				}

				int indexToSubstractValue = behaviourPattern
						.getValue(indexToSubstract);
				if (indexToSubstractValue > 0) {
					int indexToAddValue = behaviourPattern.getValue(indexToAdd);

					if (indexToSubstractValue / 2 > 0) {
						int difference = rnd.nextInt(indexToSubstractValue / 2);
						indexToSubstractValue -= difference;
						indexToAddValue += difference;

						behaviourPattern.setValue(indexToSubstract,
								indexToSubstractValue);
						behaviourPattern.setValue(indexToAdd, indexToAddValue);
					}
				}
				return true;
			}
		}
		return false;
	}

	public void setBehaviourPattern(GameState gameState,
			BehaviourPattern pattern) {
		genom.put(gameState, pattern);

	}

}